package fun.wlfj.j2t.Visitor;

import java.util.List;
import java.util.Optional;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.IntegerLiteralExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import fun.wlfj.j2t.container.ClazzContainer;
import fun.wlfj.j2t.container.MapTable;
import fun.wlfj.j2t.model.Clazz;
import fun.wlfj.j2t.model.Constructer;
import fun.wlfj.j2t.model.Element;
import fun.wlfj.j2t.model.Property;

public class MethodResolver extends VoidVisitorAdapter<Clazz> {
	
	MapTable table = new MapTable();
	
	@Override
	public void visit(AssignExpr ae, Clazz c) {
		// a = b
		super.visit(ae, c);
		System.out.println("MethodResolver> " + ae.toString());
		
		// 现在我们要查找其对应的IRTable，然后将变量创建
		List<Node> list = ae.getChildNodes();
		
		Node assignLeft = list.get(0);
		Node assignRight = list.get(1);
		
		List<Node> assignLeftList = assignLeft.getChildNodes();
		
		System.out.println(assignLeftList + ">");
		// 现在我们要顺着表找到其引用的Clazz对象
		// 既然能写出这个代码，这里一定能找到对象！
		Clazz curClazz = getCurClazz(assignLeftList.get(0));
		
		// 现在我们得到Clazz环境了 在里面添加属性
		String curPropertyName = assignLeftList.get(1).toString();
		try {
			curClazz.addProper(new Property(curPropertyName, getType(assignRight)));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.err.println("跳过该语句...");
		}
	}
	
	private Element getType(Node node) throws Exception {
		// 通过Node推断出其是什么类型的数据
		if(node instanceof IntegerLiteralExpr) {
			return new Element("int");
		} else if(node instanceof NameExpr) {
			// 我们现在要通过变量名找到类型然后返回来
			System.out.println("getType> " + node);
			return getCurClazz(node);
		} else {
			throw new Exception("No support ! Node: " + node.toString());
		}
	}
	
	private Clazz getCurClazz(Node node) {
		// 现在我们要递归解决环境问题
		if(node.getChildNodes().size() <= 1) {
			return table.get(new Element(node.toString()));
		} else {
			return (Clazz)getCurClazz(node.getChildNodes().get(0))
					.getProperByName(node.getChildNodes().get(1).toString())
					.type;
		}
	}
	
	@Override
	public void visit(ObjectCreationExpr oce, Clazz c) {
		// new
		// 我们希望知道变量是谁，构造器里的类型是什么
		// 如果系统中没有这个类型，那么就创建，否则就返回现成的。
		super.visit(oce, c);
		Optional<Node> partialExpr = oce.getParentNode();
		if(partialExpr.isPresent()) {
			Node expr = partialExpr.get();
			// 现在我们要从里面解析出来谁？什么类型？
			Node var = expr.getChildNodes().get(1);
			
			// 现在开始变量的创建
			// TODO 现在我们只支持空构造器，未来我们要支持带参数的设计
			// 我们认为其中不应该有冲突
			boolean isContained = ClazzContainer.getInstance().containsKey(oce.getTypeAsString());
			Clazz clazz = null;
			if(!isContained) {
				// 在Container中添加对应的类
				clazz = new Clazz(oce.getTypeAsString());
				ClazzContainer.getInstance().put(clazz.name, clazz);
				// 在里面添加参数
				Constructer con = new Constructer(clazz.name);
				// TODO: 检查是否添加过了
				clazz.addCons(con); // 这里我们需要检查其是否已经添加过了，其实就是检查函数签名和方法名是否相同
				for(Node n : oce.getArguments()) {
					try {
						con.addParam(getType(n));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						System.err.println("getType: Not support> " + n);
					}
				}
			}
			// 将变量名称与其连接起来，映射表肯定得不一样呢。
			table.put(new Element(var.toString()), clazz);
		}
	}
}
